/**
 * https://gitee.com/icerovah/taobao-semiauto-task
 */

 auto.waitFor();
 console.show();

const common_config = {			// 这是一些全局通用配置
	defaultDelay: 1000,			// 操作基础间隔，默认是1000毫秒
	randomDelay: 1000,			// 操作间隔随机数范围，默认是1000毫秒
	stepMultple: {				// 一些步骤的延时倍数，基于defaultDelay
		"fast": 0.25,			// 默认快速操作倍数
		"default": 1,			// 默认操作倍数
		"slow": 2,				// 默认慢速操作倍数
		"slower": 5,			// 默认更慢的操作倍数
		"slowest": 30,			// 默认更加慢的操作倍数
		"list_try": 3,			// 尝试进入任务页面的延时倍数，如果在此时间没有进入任务页面，则会提示手动操作
		"list_max": 600			// 手动进入任务页面的延时倍数，如果在此时间没有进入任务页面，则会报错
	},
	selectors: {
		// 判断是否在跳转芭芭农场中转页面
		"farmPage": className("android.widget.TextView").textContains("芭芭农场"),
		// 跳转到芭芭农场需要额外点击的按钮
		"farmBtn": className("android.view.View").text("TB1upsRKWL7gK0jSZFBXXXZZpXa-350-400"),
		// 判断是否在偷阳光页面的标识
		"stealSunPage": className("android.view.View").text(" 收取你"),
		// 进入芭芭农场集阳光提示立即去收的按钮
		"goCollectSunBtn": className("android.view.View").text("立即去收"),
		// 判断是否提示当天到上限的标识
		"completeStealSunPage": text("够啦！今天不能再偷取阳光啦"),
		// 提示偷阳光到上限页面知道了的按钮
		"completeStealSunBtn": text("知道了"),
		// 判断是否显示可偷阳光的好友列表的标识
		"stealSunFriendsPage": text("点击可偷取，去TA的农场里偷阳光吧！"),
		// 偷阳光好友列表的随机偷取按钮
		"randomStealSunBtn": text("随机偷取")
	},
	position: {
		"openSunListBtn": {		// 芭芭农场集阳光打开任务列表按钮
			"x": 965 / 1080,
			"y": 1733 / 2340
		},
		"closeSunListBtn": {	// 芭芭农场集阳光关闭任务列表按钮
			"x": 1010 / 1080,
			"y": 1289 / 2340
		},
		"stealSunBtn": {		// 芭芭农场集阳光偷阳光按钮
			"x": 770 / 1080,
			"y": 1750 / 2340
		},
		"collectSun": [			// 芭芭农场集阳光收取阳光按钮集合，前5个是偷阳光也用的
		{
			"x": 336 / 1080,
			"y": 782 / 2340
		}, {
			"x": 418 / 1080,
			"y": 1006 / 2340
		}, {
			"x": 576 / 1080,
			"y": 846 / 2340
		}, {
			"x": 587 / 1080,
			"y": 672 / 2340
		}, {
			"x": 974 / 1080,
			"y": 904 / 2340
		}, {
			"x": 180 / 1080,
			"y": 900 / 2340
		}, {
			"x": 800 / 1080,
			"y": 770 / 2340
		}
		]
	},
	back_config: [
	{
		"title": "淘宝人生",
		"selector": className("android.webkit.WebView").text("淘宝人生"),
		"type": "button",
		"x": 540 / 1080,
		"y": 1447 / 2340
	}, {
		"title": "书旗小说",
		"selector": className("android.view.View").text("请输入书名或作者名"),
		"type": "button",
		"x": 310 / 1080,
		"y": 1560 / 2340
	}, {
		"title": "天猫汽车",
		"selector": className("android.view.View").text("天猫汽车，享受一站式爱车服务"),
		"type": "button",
		"x": 540 / 1080,
		"y": 1480 / 2340
	}, {
		"title": "天猫汽车",
		"selector": className("android.view.View").text("下次可以从淘宝首页天猫汽车入口进入哦～"),
		"type": "button",
		"x": 540 / 1080,
		"y": 1530 / 2340
	}, {
		"title": "淘宝直播",
		"selector": className("android.view.View").desc("没找到想看的内容?"),
		"type": "button",
		"x": 540 / 1080,
		"y": 1600 / 2340
	}, {
		"title": "淘宝吃货",
		"selector": className("android.view.View").text("淘宝吃货").depth(13).indexInParent(1),
		"type": "button",
		"x": 540 / 1080,
		"y": 1580 / 2340
	}, {
		"title": "欢乐币",
		"selector": className("android.view.View").text("完成任务得大量欢乐币"),
		"type": "button",
		"x": 360 / 1080,
		"y": 1590 / 2340
	}, {
		"title": "欢乐币",
		"selector": className("android.view.View").textContains("提醒我领取权益，复选框"),
		"type": "button",
		"x": 360 / 1080,
		"y": 1590 / 2340
	}, {
		"title": "活力中心",
		"selector": className("android.view.View").text("确定结束训练吗？"),
		"type": "button",
		"backBtnSelector": className("android.widget.Button").text("确定退出"),
		"x": 720 / 1080,
		"y": 1500 / 2340
	}, {
		"title": "淘金币夺宝",
		"selector": className("android.widget.TextView").text("淘金币夺宝"),
		"type": "button",
		"x": 540 / 1080,
		"y": 1450 / 2340
	}, {
		"title": "淘金币夺宝",
		"selector": className("android.view.View").text("夺宝进行中"),
		"type": "button",
		"x": 540 / 1080,
		"y": 1450 / 2340
	}, {
		"title": "省钱消消消",
		// "selector": className("com.uc.webview.export.WebView").desc("WVUCWebView").depth(5).indexInParent(0),
		"selector": className("android.widget.EditText").depth(11).indexInParent(1),
		"type": "button",
		"x": 540 / 1080,
		"y": 1760 / 2340
	}
	]
};

const task_config = {			// 任务配置，可以有这些项：
	actionTitle: "",			// 任务名，必填！
	actionDelay: 24000,			// 任务默认延时，在此时间之后就算没有检测到完成也会返回
	taskMultple: 1,				// 每个步骤的默认延时倍数，默认为1
	listPageSelector: null,		// 任务页面元素Selector，用于判断是否在任务页面，必填！
	listSelector: null,			// 任务列表元素Selector，用于判断是否在任务列表，必填！
	listOpenBtnSelector: null,	// 打开任务列表的按钮Selecotr
	actionBtnClassname: "",		// 任务按钮的类名
	actionInfoOffset: 0,		// 任务描述节点与按钮的偏移量，当使用白名单或黑名单是此项应有值
	actionBtnTypeInfo: {
		"click": {
			"className": "",
			"texts": ["签到", "去签到"],
			"whiteList": [],
			"blackList": [],
			"findBtn": findActionBtn_defalut,
			"action": action_click
		},
		"browse": {
			"className": "",
			"texts": ["去浏览"],
			"whiteList": [],
			"blackList": [],
			"findBtn": findActionBtn_defalut,
			"action": action_browse
		}
	},
	actionCompleteTexts: ["任务完成", "任务已完成", "浏览完成", "浏览已完成", "全部完成"],	// 任务完成的文本
	backBtnSelector: null,		// 返回按钮的Selector
	uniqueAction: {				// 特有方法
		"main": task_default,	// 替换主流程的方法
		"openList": openList_default		// 替换打开任务列表的方法
	}
};

const user_config = [			// 各页面的单独配置
{
	actionTitle: "天猫小黑盒",
	listPageSelector: className("android.view.View").text("今日开盒任务"),
	listSelector: className("android.view.View").textContains("开盒机会"),
	listOpenBtnSelector: className("android.view.View").indexInParent(1).text("全部任务"),
	actionInfoOffset: 1,
	actionBtnTypeInfo: {
		"browse": {
			"texts": ["去完成"],
			"blackList": ["淘金币", "兑换", "去天猫"]
		}
	},
	actionCompleteTexts: ["恭喜获得"]
}, {
	actionTitle: "聚划算章鱼宝箱",
	actionDelay: 26000,
	taskMultple: 1,
	listPageSelector: className("android.view.View").text("章鱼开宝箱"),
	listSelector: textContains("好友助力即得100星星"),
	listOpenBtnSelector: className("android.widget.Image").text("TB1XiuIfiDsXe8jSZR0XXXK6FXa-621-168").depth(18).indexInParent(0),
	actionInfoOffset: 1,
	actionBtnTypeInfo: {
		"browse": {
			"texts": ["去完成"],
			"blackList": ["省钱消消消", "拍立淘", "菜鸟裹裹", "来天猫", "特价版", "高德"]
		}
	},
	actionCompleteTexts: ["发放失败", "任务已经"]
}, {
	actionTitle: "淘金币",
	listPageSelector: className("android.widget.Button").text("赚金币"),
	listSelector: className("android.view.View").text("做任务赚金币"),
	listOpenBtnSelector: className("android.widget.Button").text("赚金币"),
	taskMultple: 1.5,
	actionInfoOffset: 1,
	actionBtnTypeInfo: {
		"click": {
			"texts": ["立即签到", "打卡", "领取奖励", "领奖励"]
		},
		"browse": {
			"texts": ["去完成", "去逛逛"],
			"blackList": ["淘宝人生", "省钱消消乐", "充话费", "拍个照", "搜索", "打卡", "特卖9.9包邮", "成就签到", ".9元起", "账单", "快捷方式", "加好友", "拍一下", "通知权限", "桌面工具", "天猫APP", "下单支付", "蚂蚁庄园"],
			"findBtn": findActionBtn_taojinbi,
		},
		"browse_goodShop": {
			"texts": ["签到 +5金币", "逛10秒+10"],
			"findBtn": findBtn_goodShop,
			"action": action_browse
		},
		"click_goodShop": {
			"texts": ["关注 +10金币"],
			"findBtn": findBtn_goodShop,
			"action": action_click
		}
	},
	actionCompleteTexts: ["发放失败", "已发放", "点我领取奖励", "任务异常", "领取失败"],
	backBtnSelector: text("点我领取奖励"),
	uniqueAction: {
		"main": task_taojinbi
	}
}, {
	actionTitle: "芭芭农场 集阳光",
	listPageSelector: className("android.view.View").text("兑换好礼（每天10:00上新）"),
	listSelector: className("android.view.View").text("浏览推荐商品15秒"),
	actionBtnClassname: "android.view.View",
	actionBtnTypeInfo: {
		"browse": {
			"findBtn": findBtn_goBrowse
		},
		"shop": {
			"texts": ["去进店"],
			"findBtn": findActionBtn_defalut,
			"action": action_goIntoShop
		}
	},
	uniqueAction: {
		"main": task_yangguang
	}
}, {
	actionTitle: "芭芭农场 种水果",
	actionDelay: 25000,
	// listPageSelector: className("android.widget.Image").text("gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAAAABQABh6FO1AAAAABJRU5ErkJggg==").depth(20),
	listPageSelector: className("android.widget.Image").textMatches(/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAAAABQABh6FO1AAAAABJRU5ErkJggg==|O1CN01.*Q50s50.jpg_|O1CN01.+-2-tps-\d+-\d+\.png_\d+x\d+.jpg_/),
	// listSelector: className("android.view.View").textMatches(/每日7点\/12点|成功邀请可得/),
	listSelector: className("android.view.View").textMatches(/每日7点\/12点.*|成功邀请.*/),
	actionInfoOffset: 1,
	actionBtnTypeInfo: {
		"click": {
			// "texts": ["去领取", "..兑换", "..去兑换"]
			"texts": ["去领取", "签到", "领取奖励", "..兑换", "..去兑换"]
		},
		"browse": {
			// "texts": ["去逛逛", "去换装", "去完成"],
			"texts": ["去逛逛", "去换装", "去完成"],
			"blackList": ["支付宝", "心愿礼盒", "邀请好友"]
		}
	},
	uniqueAction: {
		"openList": openList_fuqi
	}
}
];

// mission start
task_main();

/* 以下是主流程方法 */

// 入口方法
function task_main() {

	// 判断是否为淘宝APP
	if (!packageName("com.taobao.taobao").exists()) {
		// 如果不是，报错并退出
		log_notPage();
	}

	// 遍历页面配置，寻找与当前页面相匹配的规则
	for (let i = 0; i < user_config.length; i++) {
		if (user_config[i].listPageSelector.exists()) {
			// 如果找到匹配的配置，则把它的值覆盖到task_config，以便后续使用
			task_config = copyJson(user_config[i], task_config);
			break;
		}
	}

	// 检查必填项是否都有值
	if (!task_config.actionTitle || !task_config.listPageSelector || !task_config.listSelector) {
		// 如果为空，表示未找到匹配的页面，或者配置不完整，则报错并退出执行
		log_notPage();
	}

	log("当前页面为：" + task_config.actionTitle, 1);

	// 执行主流程
	task_config.uniqueAction.main();

	// 执行完毕，显示提示信息
	log("执行完毕", 1);
	// device.vibrate(1000);

}

// 通用方法
function task_default() {

	// 检查是否在任务列表页面
	if (!task_config.listSelector.exists()) {
		// 如果不在，则打开任务列表
		openList_main();
	}
	// 寻找任务按钮
	let btnInfo = findActionBtn_main();
	// 执行任务
	while (btnInfo) {
		// 只要存在未执行任务，则循环执行
		action_main(btnInfo);
		// 执行完后，再次寻找任务按钮
		btnInfo = findActionBtn_main();
	}

}

// 芭芭农场 集阳光的特有方法
function task_yangguang() {

	console.setSize(device.width * 0.75, device.height * 0.2);

	// 判断是否有“立即去收”按钮
	if (common_config.selectors.goCollectSunBtn.exists()) {
		// 如果有，则点击
		simpleClick_btn(common_config.selectors.goCollectSunBtn.findOnce());
		sleep(common_delay(common_config.stepMultple.default, task_config.taskMultple));
	}

	// TODO:收菜

	// 打开任务列表
	simpleClick_xyJson(common_config.position.openSunListBtn);
	sleep(common_delay(common_config.stepMultple.default, task_config.taskMultple));
	// 判断是否点错了
	if (className("android.view.View").text("购买得阳光").exists()) {
		// 如果点错了，则返回
		log("点击back按键", 3);
		back();
		sleep(common_delay(common_config.stepMultple.default, task_config.taskMultple));
	}

	// 寻找任务按钮
	let btnInfo = findActionBtn_main();
	// 执行任务
	while (btnInfo) {
		// 只要存在未执行任务，则循环执行
		action_main(btnInfo);
		// 执行完后，再次寻找任务按钮
		btnInfo = findActionBtn_main();
	}

	// 关闭任务列表
	sleep(common_delay(common_config.stepMultple.default, task_config.taskMultple));
	simpleClick_xyJson(common_config.position.closeSunListBtn);
	sleep(common_delay(common_config.stepMultple.default, task_config.taskMultple));

	// 收阳光
	log("正在收取阳光", 1);
	sleep(common_delay(common_config.stepMultple.default, task_config.taskMultple));
	for (let i = 0; i < common_config.position.collectSun.length; i++) {
		simpleClick_xyJson(common_config.position.collectSun[i]);
		sleep(common_delay(common_config.stepMultple.fast, task_config.taskMultple));
	}
	log("收取阳光完成");
	sleep(common_delay(common_config.stepMultple.fast, task_config.taskMultple));

}

// 淘金币页面特有方法
function task_taojinbi() {

	sleep(common_delay(common_config.stepMultple.fast, task_config.taskMultple));
	log("正在执行能量任务", 1);
	sleep(common_delay(common_config.stepMultple.fast, task_config.taskMultple));

	// 先打开任务列表
	openList_main();

	// 连续两次打开任务列表都没有有效任务，则表示执行结束
	let loopLimit = 2;

	while (loopLimit--) {

		// 寻找任务按钮
		let btnInfo = findActionBtn_main();

		while (btnInfo) {
			// 只要存在未执行任务，则循环执行
			action_main(btnInfo);
			// 再次寻找任务按钮
			btnInfo = findActionBtn_main();
			loopLimit = 2;
		}

		// 如果直接打开找不到可以执行的任务了，则领取水滴，再次寻找
		if (loopLimit) {
			while (className("android.widget.Button").text("领取奖励").exists()) {
				sleep(common_delay(common_config.stepMultple.slow, task_config.taskMultple));
				// simpleClick("android.widget.Button", "一键领取", "领取水滴");
				sleep(common_delay(common_config.stepMultple.slow, task_config.taskMultple));
			}
		}

	}

	back();
	sleep(common_delay(common_config.stepMultple.slow, task_config.taskMultple));


	log("正在执行好店任务", 1);
	sleep(common_delay(common_config.stepMultple.fast, task_config.taskMultple));

	// 执行好店任务，因为会复用一些方法，所以把部分配置改成好店任务的模板
	task_config.listPageSelector = className("android.view.View").desc("金币好店");
	task_config.listSelector = className("android.view.View").desc("金币好店");
	task_config.uniqueAction.openList = openList_default;

	// 打开好店任务界面
	simpleClick_btn(className("android.widget.ListView").findOnce().child(0).child(0), "打开好店任务页面", 2);
	// 检查是否打开了任务界面
	waitForSelectorTwice(task_config.listSelector, task_config.taskMultple, "好店任务页面已打开", "请手动打开好店任务页面", "打开好店任务页面失败");

	// 寻找任务按钮
	let btnInfo = findActionBtn_main();
	// 执行任务
	while (btnInfo) {
		// 只要存在未执行任务，则循环执行
		action_main(btnInfo);
		// 执行完后，再次寻找任务按钮
		btnInfo = findActionBtn_main();
	}

	// 执行完好店任务后，返回到淘金币页面
	back();
	sleep(common_delay(common_config.stepMultple.default, task_config.taskMultple));

}

/* 以下是打开任务列表方法 */

// 入口方法
function openList_main(paramA) {
	// 调用各任务配置的方法
	return task_config.uniqueAction.openList(paramA);
}

// 通用方法
function openList_default() {

	// 检查是否已经在任务列表页面
	if (task_config.listSelector.exists()) {
		// 如果是，则不进行操作
		sleep(common_delay(common_config.stepMultple.default, task_config.taskMultple));
		return true;
	}
	// 检查是否配置了打开任务列表按钮
	if (!task_config.listOpenBtnSelector || !simpleClick_btn(task_config.listOpenBtnSelector.findOnce(), "点击任务列表按钮", 2) || !waitForSelector(task_config.listSelector, common_delay(common_config.stepMultple.list_try, task_config.taskMultple), "任务列表已打开")) {
		// 如果没有，或者点击失败，或者点击成功但未打开任务列表页面，则提示手动打开
		log("请手动打开任务列表", 1, true);
		waitForSelector(task_config.listSelector, common_delay(common_config.stepMultple.list_max, task_config.taskMultple), "任务列表已打开", "打开任务列表失败", -1);
	}
	// 打开任务列表之后应该等待一会儿再继续操作，以免页面刷新不及时
	sleep(common_delay(common_config.stepMultple.default, task_config.taskMultple));
	return true;

}

// 天猫农场集福气页面特有方法
function openList_fuqi() {
	// 判断当前是否已经在任务列表页面
	if (task_config.listSelector.exists()) {
		// 如果已经在了，则不进行操作
		sleep(common_delay(common_config.stepMultple.default, task_config.taskMultple));
		return true;
	}
	// 打开任务列表
	let openBtnSelectors = [
	className("android.widget.Image").depth(20).text("gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAAAABQABh6FO1AAAAABJRU5ErkJggg=="),
	className("android.widget.Image").depth(20).text("TB1vB4mgIVl614jSZKPXXaGjpXa-137-139.png_560x370Q50s50.jpg_"),
	className("android.widget.Image").depth(20).text("TB1ZbskIEH1gK0jSZSyXXXtlpXa-108-120.png_490x330Q50s50.jpg_"),
	className("android.widget.Image").depth(20).textMatches("TB.{45}Q50s50.jpg_")
	];
	let parentItem = null;
	for (let i = 0; i < openBtnSelectors.length; i++) {
		parentItem = openBtnSelectors[i].findOnce();
		if (parentItem == null) {
			continue;
		}
		parentItem = parentItem.parent();
		break;
	}
	if (parentItem != null) {
		simpleClick_btn(parentItem.child(parentItem.childCount() - 1), "正在打开任务列表", 3);
	}
	// 判断是否打开成功
	waitForSelectorTwice(task_config.listSelector, task_config.taskMultple, "任务列表已打开", "请手动打开任务列表", "打开任务列表失败");
	// 打开任务列表之后应该等待一会儿再继续操作，以免页面刷新不及时
	sleep(common_delay(common_config.stepMultple.default, task_config.taskMultple));
	return true;
}

/* 以下是回到任务页面方法 */

// 入口方法
function backToList_main() {

	// 检查是否配置了返回任务列表按钮，如果有，则点击返回按钮，如果点击成功，则等待任务列表页面
	if (!task_config.backBtnSelector || !simpleClick_btn(task_config.backBtnSelector.findOnce(), "点击返回按钮") || !waitForSelector(task_config.listPageSelector, common_delay(common_config.stepMultple.list_try, task_config.taskMultple), "已返回任务界面")) {
		// 如果没有配置返回任务列表按钮，或者点击失败，或者点击成功但没有返回到任务页面，则模拟点击back按键
		log("点击back按键(1)", 3);
		back();
	}

	if (waitForSelectors([task_config.listSelector, task_config.listPageSelector], common_delay(common_config.stepMultple.list_try, task_config.taskMultple), "已返回任务界面", "点击back按键(2)", 3)) {
		openList_main();
		return;
	}
	back();

	if (waitForSelectors([task_config.listSelector, task_config.listPageSelector], common_delay(common_config.stepMultple.list_try, task_config.taskMultple), "已返回任务界面", "点击back按键(3)", 3)) {
		openList_main();
		return;
	}
	back();

	if (waitForSelectors([task_config.listSelector, task_config.listPageSelector], common_delay(common_config.stepMultple.list_try, task_config.taskMultple), "已返回任务界面", "仍然失败", 3)) {
		openList_main();
		return;
	}

	let back_config = null;
	// 根据当前页面获取返回按钮位置的配置
	for (let i = 0; i < common_config.back_config.length; i++) {
		if (common_config.back_config[i].selector.exists()) {
			back_config = common_config.back_config[i];
			break;
		}
	}

	if (back_config == null) {
		log("未找到返回按钮配置", 3);
	} else if (back_config.type == "button") {
		log("当前页面: " + back_config.title);
		if (!back_config.backBtnSelector || !back_config.backBtnSelector.exists() || !simpleClick_btn(back_config.backBtnSelector.findOnce(), "点击页面上的返回按钮")) {
			simpleClick_xyJson(back_config, "基于坐标点击页面上的返回按钮");
		}
	} else {
		log("当前页面: " + back_config.title);
		log("点击back按键", 3);
		back();
	}

	// 再次检查是否返回到了任务页面
	waitForSelectorTwice(task_config.listSelector, task_config.taskMultple, "已返回任务界面", "请手动返回任务页面", "返回任务页面失败");

	// 打开任务列表
	openList_main();

}

/* 以下是获取任务按钮方法 */

// 入口方法
function findActionBtn_main() {

	for (let btnType in task_config.actionBtnTypeInfo) {
		let texts = task_config.actionBtnTypeInfo[btnType].texts;
		for (let i = 0; i < texts.length; i++) {
			let btnInfo = null;
			if (task_config.actionBtnTypeInfo[btnType].findBtn) {
				btnInfo = task_config.actionBtnTypeInfo[btnType].findBtn(texts[i], btnType);
			}
			if (btnInfo) {
				return btnInfo;
			}
		}
	}

}

// 通用方法
function findActionBtn_defalut(btnText, btnType) {

	// 根据关键词寻找按钮
	let btns = getSelector(task_config.actionBtnClassname, btnText).find();

	// 检查是否找到按钮
	if (!btns.length) {
		// 如果未找到，则直接进行下一项遍历操作
		return null;
	}

	// 检查是否配置了描述文本的元素偏移量
	if (!task_config.actionInfoOffset) {
		// 如果未配置，则不进行描述判断，直接返回
		return initBtnInfo(btns[0], btnType, btnText);
	}

	// 遍历每一个找到的按钮
	for (let i = 0; i < btns.length; i++) {

		// 根据配置的描述文本偏移量，获取描述文本
		let btn = btns[i];
		let actionText = null;
		if (btn.indexInParent() >= task_config.actionInfoOffset) {
			actionText = getText(btn.parent().child(btn.indexInParent() - task_config.actionInfoOffset));
		}

		// 测试描述文本是否符合白名单和黑名单的要求
		if (testActionText(actionText, task_config.actionBtnTypeInfo[btnType].whiteList, task_config.actionBtnTypeInfo[btnType].blackList)) {
			// 如果符合，则返回该按钮
			return initBtnInfo(btn, btnType, btnText, actionText);

		}
	}


	// 如果未找到，则返回null
	return null;

}

// 天猫农场集阳光页面特有方法
function findBtn_goBrowse(btnText, btnType) {
	// 判断任务冷却是否结束
	if (textContains("后开始任务").exists()) {
		// 如果已结束，则返回null
		return null;
	}
	// 根据关键词获取按钮
	let btn = getSelector(task_config.actionBtnClassname, btnText).findOnce();
	// 判断按钮是否存在
	if (btn) {
		// 如果存在，则返回该按钮
		return initBtnInfo(btn, btnType, btnText);
	}
	// 如果未找到，则返回null
	return null;
}

// 淘金币好店任务页面特有方法
function findBtn_goodShop(btnText, btnType) {
	// 根据关键词获取按钮
	let btn = getSelector(task_config.actionBtnClassname, btnText, "desc").findOnce();
	// 判断按钮是否存在
	if (btn) {
		// 如果存在，则返回该按钮
		return initBtnInfo(btn, btnType, btnText);
	}
	// 如果未找到，则返回null
	return null;
}

// 新版淘金币任务页面特有方法
function findActionBtn_taojinbi(btnText, btnType) {

	// 根据关键词寻找按钮
	let btns = getSelector(task_config.actionBtnClassname, btnText).find();

	// 检查是否找到按钮
	if (!btns.length) {
		// 如果未找到，则直接进行下一项遍历操作
		return null;
	}

	// 检查是否配置了描述文本的元素偏移量
	if (!task_config.actionInfoOffset) {
		// 如果未配置，则不进行描述判断，直接返回
		return initBtnInfo(btns[0], btnType, btnText);
	}

	// 遍历每一个找到的按钮
	for (let i = 0; i < btns.length; i++) {

		// 根据配置的描述文本偏移量，获取描述文本
		let btn = btns[i];
		let actionText = null;
		if (btn.indexInParent() >= task_config.actionInfoOffset) {
			actionText = getText(btn.parent().child(btn.indexInParent() - task_config.actionInfoOffset));
		}

		// 测试描述文本是否符合白名单和黑名单的要求
		if (testActionText(actionText, task_config.actionBtnTypeInfo[btnType].whiteList, task_config.actionBtnTypeInfo[btnType].blackList)) {
			// 如果符合，则返回该按钮
			return initBtnInfo(btn, btnType, btnText, actionText);

		}
	}


	// 如果未找到，则返回null
	return null;

}

/* 以下是执行任务的方法 */

// 入口方法
function action_main(btnInfo) {

	// 检查按钮信息是否完整
	if (!btnInfo || !btnInfo.btnItem || !btnInfo.btnType) {
		// 如果不完整，则报错，并跳过执行
		log("无效按钮", -1);
		return false;
	}

	// 判断是否有任务描述文本
	if (btnInfo.actionText && btnInfo.actionText.length > 5) {
		// 如果有任务描述文本，且文本长度>5，则打印任务描述
		log(btnInfo.actionText, 2);
	} else {
		// 否则打印按钮文本
		log("正在执行：" + btnInfo.btnText, 2);
	}

	// 点击按钮，进入任务，判断是否点击成功
	if (!simpleClick_btn(btnInfo.btnItem)) {
		// 如果不成功，则提示错误信息并返回
		log("任务跳转失败", 2);
		return false;
	}

	// 检查是否配置了该类型按钮的特有执行方法
	return task_config.actionBtnTypeInfo[btnInfo.btnType].action(btnInfo);

}

// 点击任务通用方法
function action_click(btnInfo) {
	log("点击成功");
	sleep(common_delay(common_config.stepMultple.slower, task_config.taskMultple));
	return true;
}

// 浏览任务通用方法
function action_browse(btnInfo) {
	// 等待任务完成标识
	if (!waitForMessages(task_config.actionCompleteTexts, task_config.actionDelay, "浏览完成", "浏览任务执行结束", 3)) {
		if (btnInfo.actionText && btnInfo.actionText.length > 5 && task_config.actionBtnTypeInfo[btnInfo.btnType].whiteList.length == 0) {
			task_config.actionBtnTypeInfo[btnInfo.btnType].blackList.push(btnInfo.actionText);
		}
	}
	// 在任务完成或等待到设定的时间后，返回任务列表页面
	backToList_main();
	return true;
}

// 去进店，天猫农场集阳光页面使用的特有方法
function action_goIntoShop(btnInfo) {


	// 设置打开宝箱按钮的Selector
	let btnSelectors = [];
	btnSelectors.push(className("android.view.View").descMatches(/\s*立即打开\s*/));
	btnSelectors.push(className("android.view.View").descMatches(/\s*关注店铺\s*/));
	btnSelectors.push(className("android.view.View").text("立即打开"));

	// 等待页面打开，大概5秒，判断是否已找到宝箱
	if (!waitForSelectors(btnSelectors, common_delay(common_config.stepMultple.slower, task_config.taskMultple), "已找到宝箱")) {
		// 如果未找到，则判断是否仍然是任务页面
		if (task_config.listPageSelector.exists()) {
			// 如果在，则表示任务跳转失败了，直接返回
			log("进店任务跳转失败", -1);
			return false;
		}

		// 准备开始滑动页面
		log("开始滑动页面寻找宝箱");

		// 获取设备的尺寸，以便后续的滑动操作
		let swipeX = device.width / 2;
		let swipeY = device.height / 2;

		// 设定滑动操作次数
		let limit = 60;

		// 判断是否找到宝箱
		while (!waitForSelectors(btnSelectors, common_delay(common_config.stepMultple.fast, task_config.taskMultple), "已找到宝箱")) {
			// 如果没有找到，则滑动半个屏幕
			swipe(swipeX, swipeY, swipeX, 0, common_delay(common_config.stepMultple.fast, task_config.taskMultple));
			// 判断是否滑动了指定次数
			if (--limit < 0) {
				// 如果滑动到了指定次数，则提示手动寻找宝箱
				log("请手动寻找宝箱", 1, true);
				// 最多等待1分钟，或者找到宝箱后，跳出循环
				waitForSelectors(btnSelectors, common_delay(common_config.stepMultple.slowest, task_config.taskMultple), "已找到宝箱");
				break;
			}
		}

	}

	// 尝试点击打开宝箱按钮
	let result = false;
	if (simpleClick_btn(btnSelectors[0].findOnce(), "打开宝箱") || simpleClick_btn(btnSelectors[1].findOnce(), "打开宝箱") || simpleClick_btn(btnSelectors[2].findOnce(), "打开宝箱")) {
		// 如果打开成功，则延时2秒
		result = true;
		sleep(common_delay(common_config.stepMultple.slow, task_config.taskMultple));
	} else {
		// 如果打开失败，则表示没有找到宝箱，打印提示信息并返回
		log("未在此页面找到阳光宝箱");
	}

	// 返回任务列表页面
	backToList_main();
	return result;

}

/* 以下是工具方法，只受入参和common_config的影响 */

// 通过类名、文本、文本类型获取Selector
function getSelector(classNameStr, textStr, type) {
	// 声明一个空的Selec
	let rootSelector = selector();
	// 判断是否传入了类名
	if (classNameStr) {
		// 如果传入了，则添加类名条件
		rootSelector = rootSelector.className(classNameStr);
	}
	// 判断传入的文本类型
	if (type == "desc") {
		// 如果是desc，则返回以desc为条件的Selector
		return rootSelector.desc(textStr);
	}
	if (type == "textContains") {
		// 如果是textContains，则返回以textContains为条件的Selector
		return rootSelector.textContains(textStr);
	}
	if (type == "descContains") {
		// 如果是descContains，则返回以descContains为条件的Selector
		return rootSelector.descContains(textStr);
	}
	// 如果未传入类型，则默认返回以text为条件的Selector
	return rootSelector.text(textStr);
}

// 通用日志方法
function log(message, logLevel, vibrate) {
	// 判断日志类型
	if (logLevel == -1) {
		// 出错日志
		console.error(message);
	} else if (logLevel == 1) {
		// 重要日志，一般用于整个脚本的开始和结束、提示操作等
		console.info(message);
	} else if (logLevel == 2) {
		// 普通日志，一般用于开始执行单项任务
		console.log(message);
	} else {
		// 凑数日志，一般用于显示执行过程中的一些状态
		console.verbose(message);
	}
	if (vibrate) {
		// device.vibrate(1000);
	}
}

// 报错提示已配置页面并退出
function log_notPage() {
	// 提示未知页面
	log("未知页面，退出执行。目前仅支持以下页面：", -1);
	let pageTitles = [];
	// 遍历已配置好的页面参数，将各页面的标题放入一个数组
	for (let i = 0; i < user_config.length; i++) {
		pageTitles.push(user_config[i].actionTitle);
	}
	// 列出支持的页面
	log(pageTitles, 2);
	exit();
}

// 获取操作间隔延时的方法，参数1是该步骤的延时倍数，参数2是该任务的延时倍数
function common_delay(stepMultple, taskMultple) {
	// 根据参数获取一个符合要求的随机延时
	return Math.round((common_config.defaultDelay * (stepMultple || common_config.stepMultple.default) + Math.random() * common_config.randomDelay) * (taskMultple || 1));
}

// 复制json的方法，如果字段是json则逐项复制，如果是array则逐项添加，否则就直接复制，这是一个迭代方法
function copyJson(oldJson, newJson) {
	if (!newJson) {
		return oldJson;
	}
	for (let key in oldJson) {
		let value = oldJson[key];
		if (Object.prototype.toString.call(value) === "[object Object]") {
			newJson[key] = copyJson(value, newJson[key]);
		} else if (Object.prototype.toString.call(value) === "[object Array]") {
			for (let i = 0; i < value.length; i++) {
				newJson[key].push(value[i]);
			}
		} else {
			newJson[key] = value;
		}
	}
	return newJson;
}

// 通过类名和文本来触发的点击事件
function simpleClick(classNameStr, textStr, succMessage, logLevel) {
	// 找到这些条件对应的按钮
	let btn = getSelector(classNameStr, textStr, "text").findOnce() || getSelector(classNameStr, textStr, "desc").findOnce() || getSelector(classNameStr, textStr, "textContains").findOnce() || getSelector(classNameStr, textStr, "descContains").findOnce();
	// 触发点击找到的按钮
	return simpleClick_btn(btn, succMessage, logLevel);
}

// 通过元素来触发的点击事件
function simpleClick_btn(btn, succMessage, logLevel) {

	// 检查按钮是否存在
	if (!btn) {
		// 如果不存在，跳出执行，返回false
		return false;
	}

	// 限制最多向上找5层父级元素
	let i = 5;
	// 尝试点击按钮
	let result = btn.click();
	// 循环执行，直到点击成功或者寻找父级元素达到上限
	while (!result && i--) {
		// 每次点击失败，则获取其父级元素
		btn = btn.parent();
		// 再次尝试点击
		result = btn.click();
	}

	// 检查是否点击成功和是否传入成功的提示信息
	if (result && succMessage) {
		// 如果点击成功并且有成功提示信息，则打印这条日志
		log(succMessage, logLevel || 3);
	}

	// 返回点击执行结果
	return result;

}

// 通过坐标json来触发的点击事件，传入的json应包含x和y，值为比例值
function simpleClick_xyJson(json, succMessage) {
	// 调用通过坐标来触发的点击事件方法实现
	return simpleClick_xy(json.x, json.y, succMessage);
}

// 通过坐标来触发的点击事件，传入的xy应为比例值
function simpleClick_xy(x, y, succMessage) {
	if (!common_config.thisScreenMetrics) {
		common_config.thisScreenMetrics = {};
		common_config.thisScreenMetrics.width = device.width;
		common_config.thisScreenMetrics.height = device.height;
	}
	x = common_config.thisScreenMetrics.width * x;
	y = common_config.thisScreenMetrics.height * y;
	let result = click(x, y);
	if (result && succMessage) {
		log(succMessage);
	}
	return result;
}

// 构造btnInfo
function initBtnInfo(btnItem, btnType, btnText, actionText) {
	return {
		btnItem: btnItem,
		btnType: btnType,
		btnText: btnText,
		actionText: actionText
	};
}

function getText(item) {
	if (!item) {
		return null;
	}
	let text = item.text();
	if (text && text.length > 4) {
		return text;
	}
	text = item.desc();
	if (text && text.length > 4) {
		return text;
	}
	if (item.childCount == 0) {
		return null;
	}
	text = "";
	for (let i = 0; i < item.childCount(); i++) {
		if (item.child(i).text() && item.child(i).text().length > 4) {
			text += item.child(i).text();
		} else if (item.child(i).desc() && item.child(i).desc().length > 4) {
			text += item.child(i).desc();
		}
	}
	return text;
}

// 测试任务描述是否符合白名单和黑名单的要求
function testActionText(actionText, whiteList, blackList) {

	// 判断描述文本是否为空
	if (!actionText) {
		// 如果描述信息为空，判断白名单是否有配置
		if (whiteList && whiteList.length) {
			// 如果有白名单，则返回false
			return false;
		} else {
			// 如果没有，则返回true
			return true;
		}
	}

	// 判断白名单是否有配置
	if (whiteList && whiteList.length) {
		// 如果有，则遍历白名单的各项，检查描述文本是否匹配
		for (let i = 0; i < whiteList.length; i++) {
			if (actionText.indexOf(whiteList[i]) > -1) {
				// 只要存在匹配的项，则返回true
				return true;
			}
		}
		// 如果所有项都不匹配，则返回false
		return false;
	}

	// 判断黑名单是否有配置
	if (blackList && blackList.length) {
		for (let i = 0; i < blackList.length; i++) {
			// 如果有，则遍历黑名单的各项，检查描述文本是否匹配
			if (actionText.indexOf(blackList[i]) > -1) {
				// 只要存在匹配的项，则返回false
				return false;
			}
		}
	}

	// 如果没有配置白名单和黑名单，或者通过所有检查，则返回true
	return true;

}

// 判断两次等待Selector出现的方法
function waitForSelectorTwice(userSelector, taskMultple, succMessage, errMessageA, errMessageB) {
	// 检查selector是否出现
	if (!waitForSelector(userSelector, common_delay(common_config.stepMultple.list_try, taskMultple), succMessage, errMessageA, 1)) {
		// 如果没有，提示手动操作
		// device.vibrate(1000);
		waitForSelector(userSelector, common_delay(common_config.stepMultple.list_max, task_config.taskMultple), succMessage, errMessageB, -1);
	}
}

// 等待文本的出现的方法
function waitForMessage(message, delay, succMessage, errMessage, logLevel) {
	// 声明一个文本数组
	let messages = [];
	// 将文本放入数组
	messages.push(message);
	// 调用等待文本数组出现的方法
	return waitForMessages(messages, delay, succMessage, errMessage, logLevel);
}

// 等待文本数组的出现的方法，任意文本出现后都算成功
function waitForMessages(messages, delay, succMessage, errMessage, logLevel) {
	// 声明一个选择器数组
	let userSelectors = [];
	// 遍历文本数组
	for (let i = 0 ; i < messages.length; i++) {
		// 将文本转换成选择器，将结果放入选择器数组
		userSelectors.push(textContains(messages[i]));
		userSelectors.push(descContains(messages[i]));
	}
	// 调用等待选择器数组出现的方法
	return Math.ceil(waitForSelectors(userSelectors, delay, succMessage, errMessage, logLevel) / 2);
}

// 等待选择器出现的方法
function waitForSelector(userSelector, delay, succMessage, errMessage, logLevel) {
	// 声明一个选择器数组
	let userSelectors = [];
	// 将选择器放入数组
	userSelectors.push(userSelector);
	// 调用等待选择器数组出现的方法
	return waitForSelectors(userSelectors, delay, succMessage, errMessage, logLevel);
}

// 等待选择器数组出现的方法，任意选择器出现后都算成功，这一系列等待方法的具体实现
function waitForSelectors(userSelectors, delay, succMessage, errMessage, logLevel) {

	// 如果没有传入最大延时，则设置最大延时为十分钟
	delay = delay || common_delay(common_config.stepMultple.listDelayMax);
	let targetDate = new Date().getTime() + delay;

	// 当延时还没减到0以下时，循环执行
	while (new Date().getTime() < targetDate) {
		// 遍历选择器数组，获取每一个选择器
		for (let i = 0; i < userSelectors.length; i++) {
			// 判断选择器是否出现
			if (userSelectors[i].exists()) {
				// 如果出现，判断是否传入成功提示信息
				if (succMessage) {
					// 如果有，打印这条日志
					log(succMessage);
				}
				// 等待1秒，返回成功状态
				sleep(common_delay(common_config.stepMultple.default));
				return i + 1;
			}
		}
		// 如果选择器都没有出现，等待250毫秒，再次循环
		sleep(250);
	}

	// 检查是否传入了错误提示信息
	if (errMessage) {
		// 如果有，打印这条日志
		log(errMessage, logLevel || -1);
	}
	// 检查传入的错误日志等级是否为-1
	if (logLevel == -1) {
		// 如果是，则退出脚本
		exit();
	}

	// 返回失败状态
	return 0;

}